from logging import getLogger

from zope.interface import Interface, implements
from zope.configuration import config, fields
from zope.schema import TextLine, Int, Bool, Object
from zope.component import getGlobalSiteManager

from meercat.interfaces import IOrderedRegistry
from meercat.base.registry import OrderedRegistry
from meercat.base.zcml import IRegistryItemSchema
from meercat.harvester.base.interfaces import IHarvester
from meercat.harvester.base.zcml import IFileSourceContext
from meercat.harvester.sfx import interfaces, fieldset, fields as sfxFields
from meercat.harvester.sfx.harvester import SfxHarvester



log = getLogger('meercat.harvester.sfx')

registry = getGlobalSiteManager()

class ISFXHarvesterSchema(Interface):
    name = TextLine(
        title=u'Name',
        description=u'Name to use for registering the utility',
        required=True)
    
    source = TextLine(
        title=u'Source',
        description=u'The source name for the harvester (defaults to name)',
        required=False)
        
    dialect = TextLine(
        title=u'Dialect',
        description=u'The csv dialect for the files',
        required=False,
        default=u'excel-tab')
    
    identifier = Int(
        title=u'Identifier Column',
        description=u'The zero-indexed column of the unique identifier for an instance',
        required=False,
        default=4)
    
    default_fields = Bool(
        title=u'Default fields',
        description=u'Utilize the default fields/fieldsets',
        required=False,
        default=True)

class ISFXDefaultFieldSetsSchema(Interface):
    purge = Bool(
        title=u'Purge',
        description=u'Set to true to purge existing registered fieldsets',
        required=False,
        default=False)

class ISFXFieldSetsContext(Interface):
    fieldsets = Object(schema=IOrderedRegistry)

class ISFXFieldsContext(Interface):
    fields = Object(schema=IOrderedRegistry)

class ISFXFieldSetSchema(IRegistryItemSchema):
    tag = TextLine(
        title=u'Tag',
        description=u'The XML tag to use as a root object for this fieldset',
        required=False,
        default=u'object')
    
    format = TextLine(
        title=u'Format',
        description=u'The format name to use when rendering the metadata object',
        required=False,
        default=u'sfx')
    
    purge = Bool(
        title=u'Purge',
        description=u'Set to true to purge existing registered fields',
        required=False,
        default=False)
    
class ISFXFieldSchema(IRegistryItemSchema):
    factory = fields.GlobalObject(
        title=u'Factory',
        description=u'A factory for a unique component within the context',
        required=False)

class ISFXTagFieldSchema(IRegistryItemSchema):
    tag = TextLine(
        title=u'Tag',
        description=u'The XML tag to use as for values of the field',
        required=False,
        default=u'object')
    attributes = fields.Tokens(
        title=u'Tag Attributes',
        description=u'The XML tag attributes',
        required=False,
        default=[],
        value_type=TextLine())
    filter = fields.GlobalObject(
        title=u'Filter',
        description=u'A value filter that returns either a value or None',
        required=False)

class ISFXTokenizedTagFieldSchema(ISFXTagFieldSchema):
    tokenizer = fields.GlobalObject(
        title=u'Tokenizer',
        description=u'A tokenizing function that returns a list of values',
        required=False)

class SFXHarvesterHandler(config.GroupingContextDecorator):
    implements(config.IConfigurationContext, ISFXHarvesterSchema,
               IFileSourceContext, ISFXFieldSetsContext)
    
    def __init__(self, context, name, **kwargs):
        log.debug('%s.__init__(%s,...)' % (str(self),str(context),))
        self.context = context
        self.name = name
        
        if not kwargs.get('default_fields',True):
            kwargs['fieldsets'] = OrderedRegistry(ISFXField)
        
        if 'default_fields' in kwargs:
            del kwargs['default_fields']
        
        self.kwargs = kwargs
    
    def before(self):
        log.debug('%s.before()' % (str(self),))
        super(SFXHarvesterHandler,self).before()
        self.harvester = registry.queryUtility(IHarvester, self.name, None)
        if self.harvester is None:
            self.harvester = SfxHarvester(self.kwargs.get('source', self.name))
        
        if not interfaces.ISFXHarvester.providedBy(self.harvester):
            raise TypeError('Invalid harvester type registered as %s' % (str(self.name),))
        
        if self.kwargs.get('dialect', None):
            self.harvester.dialect = self.kwargs['dialect']
        
        if self.kwargs.get('identifier', None):
            self.harvester.identifier = self.kwargs['identifier']
    
    def after(self):
        log.debug('%s.after()' % (str(self),))
        super(SFXHarvesterHandler,self).after()
        
        registry.registerUtility(self.harvester, IHarvester, self.name)
    
    def getFileSource(self):
        return self.harvester.getFileSource()
    
    def setFileSource(self, source):
        return self.harvester.setFileSource(source)
    
    @property
    def fieldsets(self):
        return self.harvester.fieldsets


class SFXDefaultFieldSetsHandler(config.GroupingContextDecorator):
    implements(config.IConfigurationContext, ISFXDefaultFieldSetsSchema, ISFXFieldSetsContext)
    
    def __init__(self, context, purge=False):
        super(SFXDefaultFieldSetsHandler, self).__init__(context)
        self.context = context
        
        if not purge:
            self.fieldsets = registry.queryUtility(interfaces.ISFXDefaultFieldSets,
                                                   default=OrderedRegistry(interfaces.ISFXFieldSet))
        else:
            self.fieldsets = OrderedRegistry(ISFXFieldSet)
    
    def after(self):
        log.debug('%s.after()' % (str(self),))
        super(SFXDefaultFieldSetsHandler, self).after()
        registry.registerUtility(self.fieldsets, interfaces.ISFXDefaultFieldSets)


class SFXFieldSetHandler(config.GroupingContextDecorator):
    implements(config.IConfigurationContext, ISFXFieldSetSchema, ISFXFieldsContext)
    
    def __init__(self, context, name, tag=None, format=None, purge=False, **kwargs):
        super(SFXFieldSetHandler, self).__init__(context)
        if not purge and name in self.context.fieldsets:
            self.fields = self.context.fieldsets[name]
        else:
            self.fields = fieldset.FieldSet()
            kwargs['component'] = self.fields
        
        if tag is not None:
            self.fields.tag = tag
        
        if format is not None:
            self.fields.format = format
        
        self.context.fieldsets.register(name, **kwargs)


def sfxFieldHandler(context, name, factory=None, component=None, **kwargs):
    log.debug('sfxFieldHandler(%s, %s, factory=%s, component=%s, **%s)' % (str(context),
                                                                           str(name),
                                                                           str(factory),
                                                                           str(component),
                                                                           str(kwargs),))
    if factory and component:
        raise ValueError('Both a factory and component cannot be specified')
    
    if factory:
        component = factory()
    
    return context.fields.register(name, component, **kwargs)


def sfxTagFieldHandler(context, name, interface=interfaces.ISFXTagField,
                       factory=sfxFields.SimpleTag,
                       local=['tag','attributes','filter'],
                       required=['tag'], **kwargs):
    lkwargs = {}
    
    for l in local:
        if l in kwargs:
            lkwargs[l] = kwargs.pop(l,None)
    
    if not name in context.fields or not interface.providedBy(context.fields[name]):
        for r in required:
            if not lkwargs.get(r, None):
                raise ValueError('%s is not a registered tag field and no %s was provided' % (name,r,))
        
        kwargs['component'] = factory(**lkwargs)
    
    sfxFieldHandler(context, name, **kwargs)
    
    if not 'component' in kwargs:
        field = self.context.fields[name]
        for key, value in lkwargs.iteritems():
            setattr(field, key, value)
        

def sfxTokenizedTagFieldHandler(context, name, **kwargs):
    return sfxTagFieldHandler(context, name, factory= sfxFields.TokenizedTag,
                              interface=interfaces.ISFXTokenizedTagField,
                              local=['tag','tokenizer','attributes','filter'],
                              required=['tag','tokenizer'], **kwargs)


